یاد بگیرید چگونه از AbortController جاوا اسکریپت برای لغو مؤثر عملیات ناهمزمان مانند درخواستهای fetch، تایمرها و موارد دیگر استفاده کنید و کدی تمیزتر و با عملکرد بهتر داشته باشید.
AbortController در جاوا اسکریپت: تسلط بر لغو عملیات ناهمزمان
در توسعه وب مدرن، عملیات ناهمزمان همه جا هستند. واکشی داده از APIها، تنظیم تایمرها و مدیریت تعاملات کاربر اغلب شامل کدهایی است که به طور مستقل و بالقوه برای مدت زمان طولانی اجرا میشوند. با این حال، سناریوهایی وجود دارد که در آنها نیاز به لغو این عملیات قبل از تکمیل شدنشان دارید. اینجاست که رابط AbortController
در جاوا اسکریپت به کمک میآید. این رابط یک روش تمیز و کارآمد برای ارسال سیگنال درخواست لغو به عملیات DOM و دیگر وظایف ناهمزمان فراهم میکند.
درک نیاز به لغو عملیات
قبل از پرداختن به جزئیات فنی، بیایید درک کنیم که چرا لغو عملیات ناهمزمان مهم است. این سناریوهای رایج را در نظر بگیرید:
- ناوبری کاربر: یک کاربر یک جستجو را آغاز میکند و یک درخواست API را فعال میکند. اگر کاربر قبل از تکمیل درخواست به سرعت به صفحه دیگری برود، درخواست اصلی دیگر اهمیتی ندارد و باید برای جلوگیری از ترافیک شبکه غیرضروری و عوارض جانبی احتمالی لغو شود.
- مدیریت تایماوت: شما برای یک عملیات ناهمزمان یک تایماوت تنظیم میکنید. اگر عملیات قبل از انقضای تایماوت تکمیل شود، باید تایماوت را لغو کنید تا از اجرای کد اضافی جلوگیری شود.
- جدا شدن کامپوننت (Unmounting): در فریمورکهای فرانتاند مانند React یا Vue.js، کامپوننتها اغلب درخواستهای ناهمزمان ارسال میکنند. هنگامی که یک کامپوننت جدا میشود (unmount)، هرگونه درخواست در حال انجام مرتبط با آن کامپوننت باید لغو شود تا از نشت حافظه و خطاهای ناشی از بهروزرسانی کامپوننتهای جدا شده جلوگیری شود.
- محدودیت منابع: در محیطهای با منابع محدود (مانند دستگاههای تلفن همراه، سیستمهای تعبیهشده)، لغو عملیات غیرضروری میتواند منابع ارزشمندی را آزاد کرده و عملکرد را بهبود بخشد. به عنوان مثال، لغو دانلود یک تصویر بزرگ اگر کاربر از آن بخش صفحه عبور کند.
معرفی AbortController و AbortSignal
رابط AbortController
برای حل مشکل لغو عملیات ناهمزمان طراحی شده است. این رابط از دو جزء کلیدی تشکیل شده است:
- AbortController: این شیء سیگنال لغو را مدیریت میکند. این شیء یک متد واحد به نام
abort()
دارد که برای ارسال سیگنال درخواست لغو استفاده میشود. - AbortSignal: این شیء سیگنالی را نشان میدهد که یک عملیات باید لغو شود. این سیگنال با یک
AbortController
مرتبط است و به عملیات ناهمزمانی که نیاز به قابل لغو بودن دارد، ارسال میشود.
استفاده پایه: لغو درخواستهای Fetch
بیایید با یک مثال ساده از لغو یک درخواست fetch
شروع کنیم:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});
// To cancel the fetch request:
controller.abort();
توضیح:
- ما یک نمونه
AbortController
ایجاد میکنیم. - ما
AbortSignal
مرتبط را ازcontroller
دریافت میکنیم. - ما
signal
را به گزینههایfetch
ارسال میکنیم. - اگر نیاز به لغو درخواست داشته باشیم،
controller.abort()
را فراخوانی میکنیم. - در بلوک
.catch()
، ما بررسی میکنیم که آیا خطا از نوعAbortError
است یا خیر. اگر چنین باشد، میدانیم که درخواست لغو شده است.
مدیریت AbortError
هنگامی که controller.abort()
فراخوانی میشود، درخواست fetch
با یک AbortError
رد (reject) خواهد شد. مدیریت صحیح این خطا در کد شما بسیار مهم است. عدم انجام این کار میتواند منجر به رد شدن پرامیسهای مدیریت نشده و رفتار غیرمنتظره شود.
در اینجا یک مثال قویتر با مدیریت خطا آورده شده است:
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
return null; // Or throw the error to be handled further up
} else {
console.error('Fetch error:', error);
throw error; // Re-throw the error to be handled further up
}
}
}
fetchData();
// To cancel the fetch request:
controller.abort();
بهترین روشها برای مدیریت AbortError:
- بررسی نام خطا: همیشه بررسی کنید که
error.name === 'AbortError'
باشد تا مطمئن شوید که نوع خطای صحیحی را مدیریت میکنید. - بازگرداندن یک مقدار پیشفرض یا پرتاب مجدد خطا: بسته به منطق برنامه شما، ممکن است بخواهید یک مقدار پیشفرض (مانند
null
) بازگردانید یا خطا را مجدداً پرتاب کنید تا در سطوح بالاتر پشته فراخوانی مدیریت شود. - پاکسازی منابع: اگر عملیات ناهمزمان منابعی (مانند تایمرها، شنوندگان رویداد) را تخصیص داده است، آنها را در کنترلکننده
AbortError
پاکسازی کنید.
لغو تایمرها با AbortSignal
از AbortSignal
همچنین میتوان برای لغو تایمرهای ایجاد شده با setTimeout
یا setInterval
استفاده کرد. این کار به کمی تلاش دستی بیشتری نیاز دارد، زیرا توابع تایمر داخلی به طور مستقیم از AbortSignal
پشتیبانی نمیکنند. شما باید یک تابع سفارشی ایجاد کنید که به سیگنال لغو گوش دهد و هنگام فعال شدن آن، تایمر را پاک کند.
function cancellableTimeout(callback, delay, signal) {
let timeoutId;
const timeoutPromise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
resolve(callback());
}, delay);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new Error('Timeout Aborted'));
});
});
return timeoutPromise;
}
const controller = new AbortController();
const signal = controller.signal;
cancellableTimeout(() => {
console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));
// To cancel the timeout:
controller.abort();
توضیح:
- تابع
cancellableTimeout
یک تابع بازگشتی (callback)، یک تأخیر (delay) و یکAbortSignal
را به عنوان آرگومان دریافت میکند. - این تابع یک
setTimeout
را تنظیم میکند و شناسه تایماوت را ذخیره میکند. - این تابع یک شنونده رویداد به
AbortSignal
اضافه میکند که به رویدادabort
گوش میدهد. - هنگامی که رویداد
abort
فعال میشود، شنونده رویداد تایمر را پاک میکند و پرامیس را رد (reject) میکند.
لغو شنوندگان رویداد (Event Listeners)
مشابه تایمرها، میتوانید از AbortSignal
برای لغو شنوندگان رویداد استفاده کنید. این کار به ویژه زمانی مفید است که میخواهید شنوندگان رویداد مرتبط با کامپوننتی که در حال جدا شدن (unmount) است را حذف کنید.
const controller = new AbortController();
const signal = controller.signal;
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
}, { signal });
// To cancel the event listener:
controller.abort();
توضیح:
- ما
signal
را به عنوان یک گزینه به متدaddEventListener
ارسال میکنیم. - هنگامی که
controller.abort()
فراخوانی میشود، شنونده رویداد به طور خودکار حذف خواهد شد.
AbortController در کامپوننتهای React
در React، میتوانید از AbortController
برای لغو عملیات ناهمزمان هنگام جدا شدن یک کامپوننت استفاده کنید. این کار برای جلوگیری از نشت حافظه و خطاهای ناشی از بهروزرسانی کامپوننتهای جدا شده ضروری است. در اینجا یک مثال با استفاده از هوک useEffect
آورده شده است:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setData(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
}
}
fetchData();
return () => {
controller.abort(); // Cancel the fetch request when the component unmounts
};
}, []); // Empty dependency array ensures this effect runs only once on mount
return (
{data ? (
Data: {JSON.stringify(data)}
) : (
Loading...
)}
);
}
export default MyComponent;
توضیح:
- ما یک
AbortController
را در داخل هوکuseEffect
ایجاد میکنیم. - ما
signal
را به درخواستfetch
ارسال میکنیم. - ما یک تابع پاکسازی از هوک
useEffect
بازمیگردانیم. این تابع هنگام جدا شدن کامپوننت فراخوانی خواهد شد. - در داخل تابع پاکسازی، ما
controller.abort()
را برای لغو درخواست fetch فراخوانی میکنیم.
موارد استفاده پیشرفته
زنجیرهسازی AbortSignalها
گاهی اوقات، ممکن است بخواهید چندین AbortSignal
را به هم زنجیر کنید. به عنوان مثال، ممکن است یک کامپوننت والد داشته باشید که نیاز به لغو عملیات در کامپوننتهای فرزند خود دارد. شما میتوانید با ایجاد یک AbortController
جدید و ارسال سیگنال آن به هر دو کامپوننت والد و فرزند به این هدف دست یابید.
استفاده از AbortController با کتابخانههای شخص ثالث
اگر از کتابخانه شخص ثالثی استفاده میکنید که به طور مستقیم از AbortSignal
پشتیبانی نمیکند، ممکن است نیاز داشته باشید کد خود را با مکانیزم لغو آن کتابخانه تطبیق دهید. این کار ممکن است شامل پیچیدن توابع ناهمزمان کتابخانه در توابع خودتان باشد که AbortSignal
را مدیریت میکنند.
مزایای استفاده از AbortController
- بهبود عملکرد: لغو عملیات غیرضروری میتواند ترافیک شبکه، استفاده از CPU و مصرف حافظه را کاهش دهد و منجر به بهبود عملکرد، به ویژه در دستگاههای با منابع محدود شود.
- کد تمیزتر:
AbortController
یک روش استاندارد و زیبا برای مدیریت لغو فراهم میکند و کد شما را خواناتر و قابل نگهداریتر میسازد. - جلوگیری از نشت حافظه: لغو عملیات ناهمزمان مرتبط با کامپوننتهای جدا شده از نشت حافظه و خطاهای ناشی از بهروزرسانی کامپوننتهای جدا شده جلوگیری میکند.
- تجربه کاربری بهتر: لغو درخواستهای نامربوط میتواند با جلوگیری از نمایش اطلاعات منسوخ و کاهش تأخیر درک شده، تجربه کاربری را بهبود بخشد.
سازگاری با مرورگرها
AbortController
به طور گسترده در مرورگرهای مدرن از جمله Chrome، Firefox، Safari و Edge پشتیبانی میشود. میتوانید برای آخرین اطلاعات، جدول سازگاری را در MDN Web Docs بررسی کنید.
پلیفیلها (Polyfills)
برای مرورگرهای قدیمیتری که به طور بومی از AbortController
پشتیبانی نمیکنند، میتوانید از یک پلیفیل استفاده کنید. پلیفیل قطعه کدی است که عملکرد یک ویژگی جدیدتر را در مرورگرهای قدیمیتر فراهم میکند. چندین پلیفیل AbortController
به صورت آنلاین در دسترس هستند.
نتیجهگیری
رابط AbortController
ابزاری قدرتمند برای مدیریت عملیات ناهمزمان در جاوا اسکریپت است. با استفاده از AbortController
، میتوانید کدی تمیزتر، با عملکرد بهتر و قویتر بنویسید که لغو عملیات را به خوبی مدیریت میکند. چه در حال واکشی داده از APIها باشید، چه در حال تنظیم تایمرها یا مدیریت شنوندگان رویداد، AbortController
میتواند به شما در بهبود کیفیت کلی برنامههای وب خود کمک کند.